﻿
using Clotho.Keyword;
using MutFormStudio;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;


namespace Clotho.Novelview {
	public partial class NovelviewControl : UserControl, UI.StudioControl {
		FontTable font_table;

		public ContextMenuStrip HeadContextMenuStrip => this.HeadCMS;

		public override string Text => $"{Nov.Base_plot.Description}-{view_name}";

		public BrushPackage cBrush { get; private set; } = BrushPackage.test;
		int size_num = 4;
		public string view_name => Nvf.Name;
		public readonly Novel Nov;
		public NovelviewControl() {
			InitializeComponent();
		}
		public Func<string> dGetCreateString { get; set; }
		public string Create_str => dGetCreateString.Invoke();

		string head_text = "";
		public Action dHeadTestChanged { get; set; }
		public string Head_text {get;set;}
		public Color Head_color => Color.LightGreen;

		readonly NovelViewFeature Nvf;

		public NovelviewControl(Novel novel, NovelViewFeature nvf) {
			InitializeComponent();
			font_table = new FontTable(this.Font);
			font_table.FontChanged += Font_table_FontChanged;
			renameTB.Visible = false;
			Nvf = nvf;
			initializeResize();
			this.DoubleBuffered = true;
			this.Nov = novel;
			foreach (var p in novel) {
				addPlotControl(p);
			}
			Nov.eAddPlot += Nov_eAddPlot;
			Nov.eRemovePlot += Nov_eRemovePlot;
			MouseWheel += NovelviewControl_MouseWheel;
			nvf.eLocationChanged += Nvf_eLocationChanged;
			this.Disposed += (send, e) => {
				Nov.eAddPlot -= Nov_eAddPlot;
				Nov.eRemovePlot -= Nov_eRemovePlot;
				nvf.eLocationChanged -= Nvf_eLocationChanged;
			};

		}


		private void NovelviewControl_MouseWheel(object sender, MouseEventArgs e) {

			if (Control.ModifierKeys == Keys.Shift) {
				if (e.Delta > 0) {
					font_table.inc();
				} else {
					font_table.dec();
				}
			}
		}

		private void Font_table_FontChanged(object sender, EventArgs e) {
			foreach(var pc in all_plot) {
				pc.DescTB.Font = font_table.Body;
				pc.TitleTB.Font = font_table.Title;
			}
		}

		List<PlotC> all_plot = new List<PlotC>(128);
		public void cleanAll() {
			selectClear();
			foreach (var pc in all_plot) {
				Controls.Remove(pc);
				pc.Dispose();
			}
			all_plot.Clear();

		}

		private void MainResizer_eResizeDone(object sender, EventArgs e) {
			if (selected_plot.Count == 0) {
				return;
			}
			Nov.addCmdKnot();
			foreach (var c in selected_plot) {
				var pc = c as PlotC;
				pc.view.Size = pc.Size;
				pc.view.Location = new Point(pc.Location.X - Pix_for_origin.X, pc.Location.Y - Pix_for_origin.Y); 
			}
		}


		private void Pc_eBeginInput(object sender, EventArgs e) {
			inputing_plot = sender as PlotC;
		}
		#region 内部的添加和删除情节框的基本操作

		/// <summary>
		/// 基本的添加新情节，如果给定的情节拥有本类对应的描述，则由本类显示之。
		/// 这个类仅仅在内部使用，不会和命令堆栈相关，不涉及撤销。
		/// </summary>
		/// <param name="plot"></param>
		private void addPlotControl(Plot plot) {
			var vf = plot.getFeature<ViewFeature>(view_name);
			if (vf != null) {//说明本显示要添加这个情节
				var pc = new PlotC(plot, this);
				pc.Parent = this;
				all_plot.Add(pc);
				this.Controls.Add(pc);
				vf.eSizeChanged = obj => pc.resetSize(Pix_for_origin);
				vf.eLocationChanged = obj => pc.resetLocation(Pix_for_origin);
				pc.resetSize(Pix_for_origin);
				 pc.resetLocation(Pix_for_origin);
				pc.eMoveHotZoneMouseDown += movePlotMouseDown;
				pc.eClickSelected += Pc_eSelected;
				pc.eBeginInput += Pc_eBeginInput;
				pc.Tag = plot;

				pc.DescTB.Font = font_table.Body;
				pc.TitleTB.Font = font_table.Title;
				pc.Show();
			}
		}
		/// <summary>
		/// 移除一个情节，如果本类正在显示某个Plot则将其移除
		/// 这个类仅仅在内部使用，不会和命令堆栈相关，不涉及撤销。
		/// </summary>
		/// <param name="plot"></param>
		private void removePlotControl(Plot plot) {
			PlotC target = all_plot.Find(pc=>pc.Tag == plot);
			if (target != null) {
				target.eClickSelected-= Pc_eSelected;
				target.eBeginInput -= Pc_eBeginInput;
				target.mainKW.MouseDown -= movePlotMouseDown;
				all_plot.Remove(target);
				selected_plot.Remove(target);
				this.Controls.Remove(target);
			}
			plot.Features.Remove(plot.getFeature<ViewFeature>(this.view_name));
		}
		
		private void rename(string new_name) {

		}
		
		#endregion



		private void Nov_eRemovePlot(object sender, EventArgs e) {
			removePlotControl((Plot)sender);
		}

		private void Nov_eAddPlot(object sender, EventArgs e) {
			addPlotControl((Plot)sender);
		}

		internal void showError(Point location, string message) {
			mainTT.ToolTipTitle = "操作错误";
			mainTT.Show(message,this,location,3000);

		}

		List<PlotC> selected_plot = new List<PlotC>(128);
		PlotC inputing_plot;
		private void NovelviewControl_Click(object sender, EventArgs e) {
			inputing_plot?.ValidateChildren();
		}
		private void selectClear() {
			foreach (var plot in Nov) {
				plot.Selected = false;
			}
			selected_plot.Clear();
		}
		private void selectChange() {
			StringBuilder sb = new StringBuilder();
			sb.Append("选中了 ");
			foreach (var pc in selected_plot) {
				pc.Is_selected = true;
				sb.Append(pc.bgd.Name); 
				sb.Append(" ");
			}
			//Console.WriteLine(sb.ToString()) ;
			resetResizer();
		}
		
		private void Pc_eSelected(object sender, System.EventArgs e) {
			selectClear();
			selected_plot.Add(sender as PlotC);
			selectChange();
		}
		private void findSelectedPlot() {
			selectClear(); 
			Rectangle select_rec = Rectangle_expand.CreateRectangle(Select_begin, Select_end);
			foreach(var pc in all_plot) {
				if((new Rectangle(pc.Location, pc.Size)).IntersectsWith(select_rec)) {
					selected_plot.Add(pc);
				}
			}
			selectChange();
			/*
			var selectedPlot =
				from pc in all_plot
				where (new Rectangle(pc.Location, pc.Size)).IntersectsWith(select_rec)
				select pc;
			selected_plot.AddRange(selectedPlot);
			*/
		}

		private void movePlotMouseDown(object sender, EventArgs e) {
			Point move_plor_last;
			Point move_plor_begin;
			if (MouseButtons == MouseButtons.Left) {
				if (!selected_plot.Contains(sender)) {
					selectClear();
					selected_plot.Add(sender as PlotC);
					selectChange();
				}
				move_plor_begin = move_plor_last = MousePosition;
				(sender as PlotC).mainKW.MouseMove += movePlotMouseMove;
				(sender as PlotC).mainKW.MouseUp += movePlotMouseUp;
				void movePlotMouseMove(object sender_2, MouseEventArgs e_2) {
					if (Control.MouseButtons == MouseButtons.Left) {
						var move_plor_end = MousePosition;
						foreach (var pc in selected_plot) {
							pc.Location = new Point(
								pc.Location.X + move_plor_end.X - move_plor_last.X,
								pc.Location.Y + move_plor_end.Y - move_plor_last.Y);
						}
						move_plor_last = move_plor_end;
						Refresh();
					}
				}
				void movePlotMouseUp(object sender_2, MouseEventArgs e_2) {
					Nov.addCmdKnot();
					foreach (var pc in selected_plot) {
						pc.view.move(move_plor_last.X - move_plor_begin.X,
							move_plor_last.Y - move_plor_begin.Y);
					}
					(sender as PlotC).mainKW.MouseMove -= movePlotMouseMove;
					(sender as PlotC).mainKW.MouseUp -= movePlotMouseUp;
					 resetResizer();
				}
			}
		}

		protected override void OnPaint(PaintEventArgs pe) {
			// Console.WriteLine(pe.ClipRectangle);
			Graphics g = pe.Graphics;
			g.SmoothingMode = SmoothingMode.AntiAlias;
			g.DrawLine(cBrush.Axis, Pix_for_origin.X, 0, Pix_for_origin.X, this.Size.Height);
			g.DrawLine(cBrush.Axis, 0, Pix_for_origin.Y, this.Size.Width, Pix_for_origin.Y);
			if (is_selecting) {
				g.DrawRectangle(cBrush.Select_border, Rectangle_expand.CreateRectangle(Select_begin, Select_end));
			}
			base.OnPaint(pe);

			/*
			if (Novel == null) {
				return;
			}
			PointF point = new PointF(100, 50);
			foreach (Plot p in Novel.Plots.Values) {
				if (p.Title != "全局信息")
					paintPlot(pe, p);
				point.Y += 40;
			}*/
		}

		public Point Pix_for_origin => Nvf.Location;
		bool is_selecting;
		Point Select_begin;
		Point Select_end;


		protected override void OnMouseDown(MouseEventArgs e) {
			base.OnMouseDown(e);
			if (e.Button == MouseButtons.Left) {
				if (Control.ModifierKeys == Keys.Control) {
					doMoveScenario();
				} else {
					doMutSelect();
				}
			}
		}
		/// <summary>
		/// 鼠标按下后，进入框选功能。
		/// </summary>
		private void doMutSelect() {
			Stopwatch s = Stopwatch.StartNew();

			is_selecting = true;
			Select_end = Select_begin = PointToClient(MousePosition);
			this.MouseMove += MoveMutSelect;
			this.MouseUp += MutSelectMouseUp;
			Console.WriteLine($"框选鼠标按下{s.ElapsedMilliseconds}");
			void MoveMutSelect(object sender, MouseEventArgs e) {
				if (MouseButtons == MouseButtons.Left) {
					Select_end = PointToClient(MousePosition);
					Console.WriteLine($"框选鼠标拖动{s.ElapsedMilliseconds}");
					Refresh();
				} 
			}
			void MutSelectMouseUp(object sender, MouseEventArgs e) {
				is_selecting = false;
				this.MouseMove -= MoveMutSelect;
				this.MouseUp -= MutSelectMouseUp;
				Console.WriteLine($"框选鼠标松开{s.ElapsedMilliseconds}");
				findSelectedPlot();
				Console.WriteLine($"完成查找{s.ElapsedMilliseconds}");
				Refresh();
			}
		}

		private void doMoveScenario() {
			Point begin = MousePosition;
			this.MouseMove += MoveScenario;
			void MoveScenario(object sender, MouseEventArgs e) {
				Point current = MousePosition;
				if (MouseButtons == MouseButtons.Left) {
					Point temp  = new Point( Pix_for_origin.X + (current.X - begin.X),
											  Pix_for_origin.Y + (current.Y - begin.Y));
					foreach (var c in Controls) {
						if (c is PlotC) {
							var pc = c as PlotC;
							var location = pc.view.Location;
							location.Offset(temp);
							pc.Location = location;
						}
					}
					Refresh();


				} else {
					this.MouseMove -= MoveScenario;
					Nov.addCmdKnot();
					Nvf.Location = new Point(Pix_for_origin.X + (current.X - begin.X),
											  Pix_for_origin.Y + (current.Y - begin.Y));
					resetResizer();
				}
			}
		}
		private void Nvf_eLocationChanged(object sender, EventArgs e) {
			foreach (var c in Controls) {
				if (c is PlotC) {
					var pc = c as PlotC;
					var location = pc.view.Location;
					location.Offset(Pix_for_origin);
					pc.Location = location;
				}
			}
			Refresh();
		}


		protected override void OnMouseWheel(MouseEventArgs e) {
			base.OnMouseWheel(e);
			if ((Control.ModifierKeys & Keys.Control) == Keys.Control) {
				if (e.Delta > 0) {
					if (size_num > 0) {
						size_num--;
					}
				} else if (e.Delta < 0) {
					if (size_num < 10) {
						size_num++;
					}
				}
				Refresh();
			} else {
				zoom += 0.05f * (e.Delta / 120);
				if (zoom < 0.1) {
					zoom = 0.1f;
				}
				if (zoom > 5) {
					zoom = 5;
				}
				Refresh();
			}
		}

		float zoom = 1;

		private void NovelView_MouseDoubleClick(object sender, MouseEventArgs e) {
			Nov.addCmdKnot();
			var plot = Nov.cmdCreatePlot();
			Point loc = new Point(e.Location.X - Pix_for_origin.X, e.Location.Y - Pix_for_origin.Y);
			ViewFeature view = null;
			Nov.doCmd(() => {
				view = new ViewFeature(plot, view_name);
				addPlotControl(plot);
				view.Location = loc;
				view.Size = new Size(120,80);
			}, () => {
				view.remove();
				removePlotControl(plot);
			});
		}




		#region 调整尺寸
		enum Direction {
			Left, Right, Top, Bottom
		};
		int resize_btn_size = 9;
		(Button, Button)[] resize_button_table;
		/// <summary>
		/// 初始化调整尺寸相关的控件
		/// </summary>
		private void initializeResize() {
			br.Visible = bl.Visible = tl.Visible = tr.Visible = false;
			br.MouseDown += resize_MouseDown;
			bl.MouseDown += resize_MouseDown;
			tl.MouseDown += resize_MouseDown;
			tr.MouseDown += resize_MouseDown;
			eResizeDone += MainResizer_eResizeDone;
			br.Size = bl.Size = tl.Size = tr.Size = new Size(resize_btn_size, resize_btn_size);
			resize_button_table = new (Button, Button)[]{
				(bl,tl),(br,tr),(tl,tr),(br,bl),
			};
		}
		IEnumerable<Control> ResizeControls => selected_plot;
		public event EventHandler eResizeDone;

		void resizeAllPlotc(RectangleF old_resize, RectangleF new_resize) {
			float proportion_x, proportion_y;
			proportion_x = (float)new_resize.Width / old_resize.Width;
			proportion_y = (float)new_resize.Height  / old_resize.Height;
			foreach (var c in ResizeControls) {
				PointF location_in_resizer = new PointF(c.Location.X - old_resize.X, c.Location.Y- old_resize.Y);
				c.Location = new Point(
					Convert.ToInt32(proportion_x * location_in_resizer.X + new_resize.X),
					Convert.ToInt32(proportion_y * location_in_resizer.Y + new_resize.Y));
				c.Size = new Size(
					Convert.ToInt32(c.Size.Width * proportion_x),
					Convert.ToInt32(c.Size.Height * proportion_y));
			}
		}

		RectangleF resize_rec => new Rectangle(resize_left, resize_top, resize_right - resize_left, resize_bottom - resize_top);

		int resize_top {
			get => tl.Location.Y+ resize_btn_size;
			set{
				if (value > resize_bottom - 100) {
					value = resize_bottom - 100;
				}
				tl.Location = new Point(tl.Location.X, value - resize_btn_size);
				tr.Location = new Point(tr.Location.X, value - resize_btn_size);
			}
		}

		int resize_left {
			get => tl.Location.X + resize_btn_size;
			set {
				if (value > resize_right - 100) {
					value = resize_right - 100;
				}
				bl.Location = new Point(value - resize_btn_size, bl.Location.Y);
				tl.Location = new Point(value - resize_btn_size, tl.Location.Y);
			}
		}
		int resize_right {
			get => br.Location.X;
			set {
				if (value < resize_left + 100) {
					value = resize_left + 100;
				}
				br.Location = new Point(value,br.Location.Y);
				tr.Location = new Point(value,tr.Location.Y);
			}
		}
		int resize_bottom {
			get => br.Location.Y;
			set {
				if (value < resize_top + 100) {
					value = resize_top + 100;
				}
				br.Location = new Point(br.Location.X, value);
				bl.Location = new Point(bl.Location.X, value);
			}
		}


		private void resize_MouseDown(object sender, MouseEventArgs e) {
			Point mouse_begin;
			Point moust_last;
			Button b = sender as Button;
			if (e.Button == MouseButtons.Left) {
				moust_last = mouse_begin = MousePosition;
				b.MouseMove += Br_MouseMove;
			}
			void Br_MouseMove(object sender2, MouseEventArgs e2) {
				if (MouseButtons == MouseButtons.Left) {
					var mouse_end = MousePosition;
					RectangleF old_resize_rec = resize_rec;
					if (br == b) {
						resize_right += mouse_end.X - moust_last.X;
						resize_bottom += mouse_end.Y - moust_last.Y;
					} else if (tr == b) {
						resize_right += mouse_end.X - moust_last.X;
						resize_top += mouse_end.Y - moust_last.Y;
					} else if (bl == b) {
						resize_left += mouse_end.X - moust_last.X;
						resize_bottom += mouse_end.Y - moust_last.Y;
					} else if (tl == b) {
						resize_left += mouse_end.X - moust_last.X;
						resize_top += mouse_end.Y - moust_last.Y;
					}
					resizeAllPlotc(old_resize_rec, resize_rec);



					moust_last = mouse_end;
					this.Refresh();
				} else {
					b.MouseMove -= Br_MouseMove;
					eResizeDone?.Invoke(this, new EventArgs());
				}
			}
		}



		internal void resetResizer() {
			if (selected_plot.Count > 0) {
				int left, right, top, bottom;
				left = selected_plot[0].Location.X;
				top = selected_plot[0].Location.Y;
				right = selected_plot[0].Location.X + selected_plot[0].Width;
				bottom = selected_plot[0].Location.Y + selected_plot[0].Height;
				foreach (var pc in selected_plot) {
					if (left >= pc.Location.X) {
						left = pc.Location.X;
					}
					if (top >= pc.Location.Y) {
						top = pc.Location.Y;
					}
					if (right <= pc.Location.X + pc.Width) {
						right = pc.Location.X + pc.Width;
					}
					if (bottom <= pc.Location.Y + pc.Height) {
						bottom = pc.Location.Y + pc.Height;
					}
					pc.BringToFront();
				}

				br.BringToFront();
				br.Location = new Point(right, bottom);
				bl.BringToFront();
				bl.Location = new Point(left - 9, bottom);
				tr.BringToFront();
				tr.Location = new Point(right, top - 9);
				tl.BringToFront();
				tl.Location = new Point(left - 9, top - 9);

				br.Visible = bl.Visible = tl.Visible = tr.Visible = true;
			} else {
				br.Visible = bl.Visible = tl.Visible = tr.Visible = false;

			}
		}
		#endregion
		#region 将情节拖进来的相关操作
		private void NovelviewControl_DragEnter(object sender, DragEventArgs e) {
			if (e.Data.GetDataPresent(typeof(Plot))){
				Plot p = e.Data.GetData(typeof(Plot)) as Plot;
				if(p.Nov == this.Nov) {
					if (all_plot.Exists(pc => p == pc.bgd)) {
						e.Effect = DragDropEffects.None;
						//TODO 显示移动的东西
					} else {
						e.Effect = DragDropEffects.Move;
					}

				} else {
					e.Effect = DragDropEffects.Copy;

				}
			} else {
				e.Effect = DragDropEffects.None;
			}
		}
		private void NovelviewControl_DragDrop(object sender, DragEventArgs e) {
			if(e.Effect == DragDropEffects.None) {
				return;
			} else if(e.Effect == DragDropEffects.Move) {
				Plot p = e.Data.GetData(typeof(Plot)) as Plot;
				addExistPlot(p, e);

			} else if(e.Effect == DragDropEffects.Copy){
				Console.WriteLine("发生了复制Plot");			
			}
		}
		private void addExistPlot(Plot plot, DragEventArgs e) {
			Nov.addCmdKnot();
			Point m_in_c = this.PointToClient(Control.MousePosition);
			Point loc = new Point(m_in_c.X  - Pix_for_origin.X, m_in_c.Y - Pix_for_origin.Y);
			ViewFeature view = null;
			Nov.doCmd(() => {
				view = new ViewFeature(plot, view_name);
				addPlotControl(plot);
				view.Location = loc;
				view.Size = new Size(120, 80);
			}, () => {
				view.remove();
				removePlotControl(plot);
			});
		}

		#endregion

		private void 重命名ToolStripMenuItem_Click(object sender, EventArgs e) {
			this.renameTB.Visible = true;
			renameTB.Focus();
			renameTB.Text = Nvf.Name;
		}

		private void renameTB_Validating(object sender, System.ComponentModel.CancelEventArgs e) {
			Nov.addCmdKnot();
			Nvf.Name = renameTB.Text;
			renameTB.Visible = false;
		}

		private void renameTB_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
			if(e.KeyCode == Keys.Enter) {
				Validate();
			}
		}

	}
	static class expandPointF {
		static public PointF DotMultiply(this PointF p1, float rate) {
			var p2 = new PointF(p1.X * rate, p1.Y * rate);
			return p2;
		}
		static public PointF DotMultiply(this Point p1, float rate) {
			var p2 = new PointF(p1.X * rate, p1.Y * rate);
			return p2;
		}
	}
}
